function int2FixLenS( n, len) {
    s = String( n);
    cn = len - s.length;
    switch( cn) {
    case 1:
        return "0" + s;
    case 2:
        return "00" + s;
    case 3:
        return "000" + s;
    case 4:
        return "0000" + s;
    }
    return s;
}

function genSpriteImagePath( baseUri, par, act, sd, n, prefix) {
    return baseUri + par + "/" + act + "/" +
        String( sd) + int2FixLenS( n, 4) + prefix;
}

function SpriteDesc() {
    this._direction = 0;
    this._partList = new Array();
    this._actList = new Array();
}

function parseSpriteDesc( text) {
    var lines = text.split("\n");
    var desc = new SpriteDesc();
    for ( var i = 0; i < lines.length; ++i) {
        var fields = lines[i].split( " ");
        if ( fields[0] == "direction") {
            desc._direction = parseInt( fields[1]);
        }
        else if ( fields[0] == "parts") {
            for ( var j = 1; j < fields.length; ++j) {
                desc._partList.push( fields[j]);
            }
        }
        else if ( fields[0] == "actions") {
            for ( var j = 1; j < fields.length; ++j) {
                var actpair = fields[j].split( "%");
                desc._actList[actpair[0]] = actpair[1];
            }
        }
    }
    return desc;
}

function SImgDesc() {
    this._ox = 0;
    this._oy = 0;
    this._w = -1;
    this._h = -1;
}

function SpriteImage(){
    this._image = new Image();
    this._image._state = 0;
    this._desc = null;
}

function SpriteAction( act, sd) {
    this._act = act;
    this._sd = sd;
    this._simages = new Array();
    this._isOk = false;
}

SpriteAction.prototype = {
addImgDesc : function( n , desc) {
    var si = this._simages[n];
    if ( (typeof si) == 'undefined') {
        si = new SpriteImage();
        this._simages[n] = si;
    }
    si._desc = desc;        
},

loadImages : function( baseUri, partName) {
    for ( var i = 0; i < this._simages.length; ++i) {
        var si = this._simages[i];
        if ( si._image._state == 0) {
            si._image._state = 2;
            si._image.onload = function() {
                this._state = 1;
            }
            si._image.src = genSpriteImagePath( baseUri, partName, this._act, this._sd, i + 1, ".png");
            var s = si._image.src;
        }
    }
},

loaded : function( baseUri, partName) {
    if ( this._isOk)
        return true;
    var len = this._simages.length;
    if ( len < 1)
        return false;
    var si = this._simages[0];
    if ( si._desc == null)
        return false;
    if ( si._image._state == 0) {
        this.loadImages( baseUri, partName);
        return false;
    }
    else if ( si._image._state != 1)
        return false;
    for ( var i = 1; i < len; ++i) {
        si = this._simages[i];
        if ( si._image._state != 1 || si._desc == null)
            return false;
    }
    this._isOk = true;
    return true;
},

}   // end SpriteAction.prototype

function SpriteData( baseUri) {
    this._isOk = false;
    this._loadedCbs = new Array();
    
    this._baseUri = baseUri;
    this._maxDrtN = 8;
    this._drtN = 8;
    this._body = new Array();
    this._bodyDescs = new Array();   // key: actName+sd, value: SImgDesc Array 
    this._partsN = 1;
    this._partsList = null;
    this._partsDescsList = null;     // key: partName, value: Array{ actname+sd, SImgDescArray}
    this._actVs = null;
}

SpriteData.prototype.addLoadedCallback = function( callback) {
    this._loadedCbs.push( callback);
}

SpriteData.prototype.onLoadOk = function( drtN, partsList, actVs) {
    this._isOk = true;
    this._drtN = drtN;
    this._partsN = partsList.length;
    if ( this._partsN > 1) {
        this._partsList = new Array();
        this._partsDescsList = new Array();
        for ( var i = 0; i < partsList.length; ++i) {
            this._partsList[partsList[i]] = new Array();
            this._partsDescsList[partsList[i]] = new Array();
        }
        this._actVs = actVs;
    }
    for ( var i = 0; i < this._loadedCbs.length; ++i) {
        var cb = this._loadedCbs[i];
        cb();
    }
    this._loadedCbs = null;
}

function genSptActKey( act, sd) {
    return act + String( sd);
}

function parseSImgDesc( text, maxDrtN) {
    var mulDrtDescs = new Array();
    for ( var i = 0; i < maxDrtN; ++i)
        mulDrtDescs[i] = new Array();
            
    var lines = text.split( "\n");
    for ( var k in lines) {
        var fields = lines[k].split( " ");
        if ( fields.length < 5)
            continue;
        var drt = parseInt( fields[0].substr( 0, 1));
 
        var imgDesc = new SImgDesc();            
        imgDesc._ox = parseInt( fields[1]);
        imgDesc._oy = parseInt( fields[2]);
        imgDesc._w = parseInt( fields[3]);
        imgDesc._h = parseInt( fields[4]);
        
        var descs = mulDrtDescs[drt];
        descs.push( imgDesc);
    }

    return mulDrtDescs;
}

SpriteData.prototype.loadSImgDesc = function( partActs, partDescs, partName, actName) {
    var descUri = this._baseUri + partName + "/" + actName + "/" + "desc.txt";
    var maxDrtN = this._maxDrtN;
    $.get( descUri, function( text, status) {
        if ( status == "success") {
            var mulDrtDescs = parseSImgDesc( text, maxDrtN);
            
            for ( var i = 0; i < maxDrtN; ++i) {
                var descs = mulDrtDescs[i];
                if ( descs.length <= 0)
                    continue;
                var key = genSptActKey( i, actName);
                partDescs[key] = descs;
                var act = partActs[key];
                if ( (typeof act) == 'undefined') {
                    act = new SpriteAction( actName, i);
                    partActs[key] = act;
                }
                for ( var j = 0; j < descs.length; ++j) {
                    act.addImgDesc( j, descs[j]);
                }
            }
        }
    });
}

SpriteData.prototype.getPartAct = function( partName, partActs, partDescs, actName, sd) {
    var key = genSptActKey( sd, actName);
    var act = partActs[key];
    if ( (typeof act) == 'undefined') {
        act = new SpriteAction( actName, sd);
        partActs[key] = act;
    }
    var descs = partDescs[key];
    if ( (typeof descs) == 'undefined') {
        this.loadSImgDesc( partActs, partDescs, partName, actName);
    }
    else {
        if ( act._simages[0]._desc == null && descs[0]._w >= 0) {
            for ( var i = 0; i < act.length && i < descs.length; ++i) {
                act._simages[i]._desc = descs[i];
            }
        }
    }
    return act;
}

SpriteData.prototype.getBodyAct = function( actName, sd) {
    return this.getPartAct( "body", this._body, this._bodyDescs, actName, sd);
}

SpriteData.prototype.getAct = function( partName, actName, sd) {
    if ( partName == "body") {
        return this.getPartAct( partName, this._body, this._bodyDescs, actName, sd);
    }
    else {
        var part = this._partList[partName];
        if ( (typeof part) == 'undefined')
            return null;
        else
            return this.getPartAct( partName, part, this._partsDescsList[partName], actName, sd);
    }
}

function SpriteDataMgr() {
    this._sptDatas = new Array();
}

var _sptDataMgr_ = new SpriteDataMgr();

SpriteDataMgr.prototype.getData = function( sptUri, callback) {
    var sptData = this._sptDatas[sptUri];
    
    if ( (typeof sptData) == 'undefined') {
        sptData = new SpriteData( sptUri);
        this._sptDatas[sptUri] = sptData;
    }
    else if ( sptData._isOk)
        return sptData;
    
    sptData.addLoadedCallback( callback);
        
    var sptDesc = sptUri + "desc.txt";
    $.get( sptDesc, function( text, status){
        if ( status == "success") {
            var desc = parseSpriteDesc( text);
            if ( desc._direction > 0)
                sptData.onLoadOk( desc._direction, desc._partList, desc._actList);
        }
    }, "text");
    
    return sptData;
}

function Sprite() {
    this._actName = "stand1";
    this._drt = 3;
    this._sptData = null;
    this._bodyAct = null;
    this._partActs = null;
    this._playV = 1.0 / 8.0;
    this._curFrame = 0;
    this._curFrameTime = 0;
}

Sprite.prototype = {
_init : function( baseUri) {
    var _This = this;
    this._sptData = _sptDataMgr_.getData( baseUri, function() {
        _This._loadAct();
    });   
},

_loadAct : function() {
    var sptData = this._sptData;
    if ( !sptData._isOk)
        return;
    if ( (typeof sptData._actVs[this._actName]) == 'undefined')
        this._actName = "run";
    this._playV = 1.0 / sptData._actVs[this._actName];
    this._curFrame = 0;
    this._curFrameTime = 0;
    this._bodyAct = sptData.getBodyAct( this._actName, this._drt);
    if ( !this._bodyAct._isOk)
        this._bodyAct.loadImages( sptData._baseUri, "body");
    if ( this._partActs == null)
        return;
    for ( var k in this._partActs) {
        var act = sptData.getPartAct( k, this._actName, this._drt);
        if ( !act._isOk)
            act.loadImages( sptData._baseUri, k);
        this._partActs[k] = act;
    }
},

setActDrt : function( actName, drt) {
    if ( this._actName == actName && this._drt == drt)
        return;
    if ( drt < 0 || drt > 7)
        return;
    this._actName = actName;
    this._drt = drt;
    this._loadAct();
},

setAction : function( actName) {
    if ( this._actName == actName)
        return;
    this._actName = actName;
    this._loadAct();
},

setDirection : function( drt) {
    if ( this._drt == drt)
        return;
    if ( drt < 0 || drt > 7)
        return;
},

setDrtByVector : function( identX, identY, actName) {
    var x = Math.abs( identX);
    var y = identY;
    var drt = 4;
    
    switch ( this._sptData._drtN) {
    case 1: {
        drt = 4;
    }break;
    
    case 3: {
        if ( x >= 0) {
            if ( identY >= 0 && y >= x)
                drt = 4;
            else
                drt = 3;
        }
        else {
            if ( identY >= 0 && y >= x)
                drt = 4;
            else
                drt = 3;
        }
    }break;
    
    case 4: {
        if ( x >= 0 ) {
            if ( y >= 0)
                drt = 3;
            else
                drt = 1;
        }
        else {
            if ( y >= 0)
                drt = 7;
            else
                drt = 5;
        }
    }break;
    
    default: {
        if ( x < 0.00001) {
            if ( y < 0)
                drt = 0;
            else
                drt = 4;
        }
        else {
            var tanValue = y / x;
            if ( tanValue > 3.7320508)
                drt = 4;
            else if ( tanValue >= 0.4663076)
                drt = 3;
            else if ( tanValue >= -0.4663076)
                drt = 2;
            else if ( tanValue >= -3.7320508)
                drt = 1;
            else
                drt = 0;
            if ( identX < 0 ) {
                switch( drt) {
                case 1 : drt = 7; break;
                case 2 : drt = 6; break;
                case 3 : drt = 5; break;
                }
            }
        }
    }break;
    
    }   // end switch

    if ( (typeof actName) == 'undefined')
        this.setDirection( drt);
    else
        this.setActDrt( actName, drt);
},

update : function( tickTime) {
    this._curFrameTime += tickTime;
    if ( this._curFrameTime > this._playV) {
        this._curFrameTime -= this._playV;
        ++this._curFrame;
        if ( this._curFrame >= this._bodyAct._simages.length)
            this._curFrame = 0;
    }
},

_draw : function( context, x, y) {
    var bodyAct = this._bodyAct;
    if ( !bodyAct || !bodyAct.loaded( this._sptData._baseUri, "body"))
        return;
    var simg = bodyAct._simages[this._curFrame]
    var desc = simg._desc;
    context.drawImage( simg._image, x + desc._ox, y + desc._oy);
},


}   // end Sprite.prototype

function PosSprite() {
    this.x = 0;
    this.y = 0;
    this._map = null;
}

PosSprite.prototype = new Sprite();
PosSprite.prototype.constructor = PosSprite;

PosSprite.prototype.init = function( baseUri, map) {
    this._init( baseUri);
    if ( typeof map != 'undefined')
        this._map = map;
}

PosSprite.prototype.setPos = function( x, y) {
    this.x = x;
    this.y = y;
}

PosSprite.prototype.getPos = function() {
    return new Point2D( this.x, this.y);
}

PosSprite.prototype.draw = function( context) {
    this._draw( context, this.x, this.y);    
}
